package tracker.model

import bamboo.util._
import com.db4o._
import com.db4o.query.Query

class Project(val name: String) {
  override def toString() = name
}

class Task(val project: Project, val name: String) {
  override def toString() = name
}

class Activity(val task: Task, val started: java.util.Date, var finished: java.util.Date) {
  
  var notes: String = _
  
  override def toString() = "" + task.name + "[" + elapsed + "]"
   
  def elapsed = new TimeSpan(finished.getTime() - started.getTime())
}

class Tasks(criteria: List[Query=>Query]) extends LazyQuery[Tasks, Task](criteria) {
  
  override def factory = new Tasks(_)
  
  def forProject(p: Project) = {
    constrain { q=>
      q.descend("project").constrain(p)
      q
    }
  }
}

class Activities(criteria: List[Query=>Query]) extends LazyQuery[Activities, Activity](criteria) {
  
  override def factory = new Activities(_)
  
  def from(date: java.util.Date) = {
    constrain { q=>
      q.descend("started").constrain(date).smaller().not
      q
    }
  }
  
  def forProject(p: Project) = {
    constrain { q=>
      q.descend("task").descend("project").constrain(p)
      q
    }
  }
  
  def totalTime = this.map(_.elapsed).reduceLeft[TimeSpan](_ + _)
}

class Projects(criteria: List[Query=>Query]) extends LazyQuery[Projects, Project](criteria) {
  
  override def factory = new Projects(_)
  
}

class TimeTrackingSystem(dataFile: bamboo.io.FileOrString) {

  private val _container = Db4o.openFile(configuration, dataFile.file.getAbsolutePath)
  
  val activities = new Activities(List(q=>activityQuery))
  
  val tasks = new Tasks(List(q=>taskQuery))
  
  val projects = new Projects(List(q=>projectQuery))
  
  private def configuration = {
    val config = Db4o.newConfiguration()
    config.objectClass(classOf[Activity]).objectField("started").indexed(true)
    config
  }
  
  def addProject(p: Project) = {
    set(p)
  }
  
  def addTask(t: Task) = {
    set(t)
  }
  
  def addActivity(a: Activity) = {
    set(a)
  }
  
  def discard(a: Activity) = {
    _container.delete(a)
    _container.commit()
  }
  
  import Dates._
  
  def startProgress(t: Task) = {
    set(new Activity(t, now, now))
  }
  
  def flush(current: Activity) = {
    current.finished = now
    set(current)
  }
  
  def update(o: Object) = {
    if (_container.ext.getID(o) < 1) {
      throw new IllegalArgumentException("Unknown object: " + o)
    }
    set(o)
  }
  
  private def activityQuery = {
    sortedQuery(classOf[Activity], "started")
  }
  
  private def taskQuery = {
    sortedByName(classOf[Task])
  }
  
  private def projectQuery  = {
    sortedByName(classOf[Project])
  }
  
  private def set[T](o: T) = {
    _container.set(o)
    _container.commit()
    o
  }
  
  private def sortedByName[T](klass: Class[T]) = {
    sortedQuery(klass, "name")
  }
  
  private def sortedQuery[T](klass: Class[T], sortField: String) = {
    val q = query(klass)
    q.descend(sortField).orderAscending
    q
  }
  
  private def query[T](klass: Class[T]) = {
    val q = _container.query()
    q.constrain(klass)
    q
  }
  
  def dispose() = {
    _container.close()
  }

}